package com.example.handler.declare;

import com.example.constant.Attribute;
import com.example.constant.QualifiedName;
import com.example.util.CustomNotifier;
import com.example.util.PsiElementUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import org.apache.commons.lang3.StringUtils;

import java.util.Objects;

/**
 * @author Aaron
 * @since 2021/8/29 14:04
 * <p>描述：</p>
 */
public class FeignImplGoToDeclareHandler extends BaseGoToDeclareHandler {

    private final AnActionEvent anActionEvent;

    private final Project project;
    private final Module module;
    private final String referenceUrl;

    public FeignImplGoToDeclareHandler(AnActionEvent anActionEvent, PsiElement currentPsiElement) {
        this.anActionEvent = anActionEvent;
        PsiMethod currentPsiMethod = (PsiMethod) currentPsiElement;
        // 获取当前 Project
        this.project = anActionEvent.getProject();
        Objects.requireNonNull(project, "Can't get the project by the action event.");

        ProjectFileIndex fileIndex = ProjectFileIndex.getInstance(project);
        this.module = fileIndex.getModuleForFile(currentPsiMethod.getContainingFile().getVirtualFile());
        Objects.requireNonNull(project, "Can't get the module by the action event.");

        // 获取 url
        this.referenceUrl = PsiElementUtil.getReferenceUrl(currentPsiMethod, QualifiedName.Annotation.REQUEST_MAPPING,
                Attribute.RequestMapping.VALUE);

        CustomNotifier.info(anActionEvent.getProject(), "Going to " + referenceUrl);
    }

    @Override
    public void doHandle() {
        super.doHandle();

        // 遍历 project
        VirtualFile[] contentRoots = ProjectRootManager.getInstance(project).getContentRoots();
        for (VirtualFile virtualFile : contentRoots) {
            PsiDirectory psiDirectory = PsiManager.getInstance(project).findDirectory(virtualFile);
            if (null == psiDirectory) {
                continue;
            }

            boolean isSuccess = this.doGoToDeclare(psiDirectory);
            if (isSuccess) {
                return;
            }
        }

        CustomNotifier.error(anActionEvent.getProject(), "No target was found.");
    }

    /**
     * 跳转实现
     *
     * @param psiDirectory pom.xml 文件 的父目录
     * @return 是否已跳转到实现
     */
    private boolean doGoToDeclare(PsiDirectory psiDirectory) {
        // 由于 PsiDirectory 的目录和文件需要分开获取，所以这里要分开处理
        // 获取该目录的子目录，递归处理
        PsiDirectory[] subdirectories = psiDirectory.getSubdirectories();
        for (PsiDirectory subdirectory : subdirectories) {
            if (this.doGoToDeclare(subdirectory)) {
                return true;
            }
        }

        // 获取该目录下的文件
        PsiFile[] files = psiDirectory.getFiles();
        // 遍历该目录下的所有文件
        for (PsiFile file : files) {
            // 不是 Java 类型的文件直接跳过
            if (!(file.getFileType() instanceof JavaFileType)) {
                continue;
            }

            if (this.doGoToFile(file)) {
                return true;
            }
        }
        return false;
    }

    private boolean doGoToFile(PsiFile file) {
        // 获取该文件的子 Psi 元素
        PsiElement[] children = file.getChildren();
        for (PsiElement child : children) {
            if (!(child instanceof PsiClass)) {
                // 这里只处理类型为 PsiClass 的元素，除此之外还有 PsiPackage、PsiWhiteSpace、PsiImportList 等
                continue;
            }

            PsiAnnotation feignAnnotation = PsiElementUtil.getAnnotationAtClass(child, QualifiedName.Annotation.FEIGN_CLIENT);
            if (feignAnnotation == null) {
                // Feign 接口需要使用 @FeignClient 注解标记
                continue;
            }

            String moduleName = PsiElementUtil.getAttributeValueOfAnnotationAtClass(child, QualifiedName.Annotation.FEIGN_CLIENT,
                    Attribute.FeignClient.NAME);
            moduleName = StringUtils.isBlank(moduleName)
                    ? PsiElementUtil.getAttributeValueOfAnnotationAtClass(child, QualifiedName.Annotation.FEIGN_CLIENT, Attribute.FeignClient.VALUE)
                    : moduleName;
            if (!Objects.equals(module.getName(), moduleName)) {
                continue;
            }

            for (PsiElement psiElement : child.getChildren()) {
                if (!(psiElement instanceof PsiMethod)) {
                    continue;
                }

                String referenceUrl = PsiElementUtil.getReferenceUrl(psiElement, QualifiedName.Annotation.FEIGN_CLIENT, Attribute.FeignClient.PATH);
                // Url 完全相同，或者替换掉分隔符 / 后相同
                if (Objects.equals(referenceUrl.replaceAll("/", ""), this.referenceUrl.replaceAll("/", ""))) {
                    String[] split = referenceUrl.split("/");
                    return this.doLocate(project, split[split.length - 1], file);
                }
            }
        }

        return false;
    }

}
